package alipay

import (
	"context"
	"crypto/rsa"
	"encoding/json"
	"fmt"
	"os"
	"time"

	"gitee.com/wosylf/ltool"
	"gitee.com/wosylf/ltool/pkg/util"
	"gitee.com/wosylf/ltool/pkg/xhttp"
	"gitee.com/wosylf/ltool/pkg/xlog"
	"gitee.com/wosylf/ltool/pkg/xpem"
	"gitee.com/wosylf/ltool/pkg/xrsa"
)

type Client struct {
	AppId              string
	AppCertSN          string
	AliPayPublicCertSN string
	AliPayRootCertSN   string
	AliRas2Value       []byte
	AliPayeeUserId     string //商家收款id
	AliContentKey      string //用于加解密手机号用的
	ZhimaId            string //这个芝麻信息才用到
	ReturnUrl          string
	NotifyUrl          string
	Charset            string
	SignType           string
	AppAuthToken       string
	Assessment         int8  //租押分离金额
	Istest             int8  //是否为测试
	Aid                int64 //租押分离金额
	IsProd             bool
	privateKey         *rsa.PrivateKey
	aliPayPublicKey    *rsa.PublicKey // 支付宝证书公钥内容 alipayCertPublicKey_RSA2.crt
	autoSign           bool
	DebugSwitch        ltool.DebugSwitch
	location           *time.Location
}

func F私钥新建支付宝链接(appid, private string) (client *Client, err error) {
	key := xrsa.FormatAlipayPrivateKey(private)
	priKey, err := xpem.DecodePrivateKey([]byte(key))
	if err != nil {
		return nil, err
	}
	client = &Client{
		AppId:       appid,
		Charset:     UTF8,
		SignType:    RSA2,
		IsProd:      true,
		privateKey:  priKey,
		DebugSwitch: ltool.DebugOff,
	}
	return
}

func F新建链接路径(私钥 string, AliPublicCerPath, AliRootCerPath, AliRas2Path string) (client *Client, err error) {
	AliPublicCerValue, err := os.ReadFile(AliPublicCerPath)
	if err != nil {
		return
	}
	AliRootCerValue, err := os.ReadFile(AliRootCerPath)
	if err != nil {
		return
	}
	AliRas2Value, err := os.ReadFile(AliRas2Path)
	if err != nil {
		return
	}
	return F新建链接(私钥, AliPublicCerValue, AliRootCerValue, AliRas2Value)
}

// 证书模式，公私钥模式暂时不管吧
func F新建链接(私钥 string, AliPublicCerValue, AliRootCerValue, AliRas2Value []byte) (client *Client, err error) {
	key := xrsa.FormatAlipayPrivateKey(私钥)
	priKey, err := xpem.DecodePrivateKey([]byte(key))
	if err != nil {
		return nil, err
	}
	client = &Client{
		//AppId:       appId,
		Charset:      UTF8,
		SignType:     RSA2,
		IsProd:       true,
		AliRas2Value: AliRas2Value,
		privateKey:   priKey,
		DebugSwitch:  ltool.DebugOff,
	}
	err = client.SetCertSnByContent(AliPublicCerValue, AliRootCerValue, AliRas2Value)
	//添加一下证书信息
	//err = client.SetCertSnByPath("alipay/cert/appCertPublicKey_2021003120647634.crt", "alipay/cert/alipayRootCert.crt", "alipay/cert/alipayCertPublicKey_RSA2.crt")
	if err != nil {
		fmt.Println(err.Error(), "usr 新建链接报错了")
		return nil, err
	}
	return
}

// 开启请求完自动验签功能（默认不开启，推荐开启，只支持证书模式）
//
//	注意：只支持证书模式
//	alipayPublicKeyContent：支付宝公钥证书文件内容[]byte
func (a *Client) AutoVerifySign(alipayPublicKeyContent []byte) {
	pubKey, err := xpem.DecodePublicKey(alipayPublicKeyContent)
	if err != nil {
		xlog.Errorf("AutoVerifySign(%s),err:%+v", alipayPublicKeyContent, err)
	}
	if pubKey != nil {
		a.aliPayPublicKey = pubKey
		a.autoSign = true
	}
}

// Deprecated
//
//	推荐使用 PostAliPayAPISelfV2()
//	示例：请参考 client_test.go 的 TestClient_PostAliPayAPISelf() 方法
func (a *Client) PostAliPayAPISelf(ctx context.Context, bm ltool.BodyMap, method string, aliRsp interface{}) (err error) {
	var bs []byte
	if bs, err = a.doAliPay(ctx, bm, method); err != nil {
		return err
	}
	if err = json.Unmarshal(bs, aliRsp); err != nil {
		return err
	}
	return nil
}

// Deprecated
//
//	推荐使用 RequestParam()
func (a *Client) GetRequestSignParam(bm ltool.BodyMap, method string) (string, error) {
	return a.RequestParam(bm, method)
}

// RequestParam 获取支付宝完整请求参数包含签名
//
//	注意：biz_content 需要自行通过bm.SetBodyMap()设置，不设置则没有此参数
func (a *Client) RequestParam(bm ltool.BodyMap, method string) (string, error) {
	var (
		bodyBs []byte
		err    error
		sign   string
	)
	// check if there is biz_content
	bz := bm.GetInterface("biz_content")
	if bzBody, ok := bz.(ltool.BodyMap); ok {
		if bodyBs, err = json.Marshal(bzBody); err != nil {
			return "", fmt.Errorf("json.Marshal(%v)：%w", bzBody, err)
		}
		bm.Set("biz_content", string(bodyBs))
	}
	bm.Set("method", method)

	// check public parameter
	a.checkPublicParam(bm)

	// check sign
	if bm.GetString("sign") == "" {
		sign, err = GetRsaSign(bm, bm.GetString("sign_type"), a.privateKey)
		if err != nil {
			return "", fmt.Errorf("GetRsaSign Error: %w", err)
		}
		bm.Set("sign", sign)
	}

	if a.DebugSwitch == ltool.DebugOn {
		xlog.Debugf("Alipay_Request: %s", bm.JsonBody())
	}
	return bm.EncodeURLParams(), nil
}

// PostAliPayAPISelfV2 支付宝接口自行实现方法
//
//	注意：biz_content 需要自行通过bm.SetBodyMap()设置，不设置则没有此参数
//	示例：请参考 client_test.go 的 TestClient_PostAliPayAPISelfV2() 方法
func (a *Client) PostAliPayAPISelfV2(ctx context.Context, bm ltool.BodyMap, method string, aliRsp interface{}) (err error) {
	var (
		bs, bodyBs []byte
	)
	// check if there is biz_content
	bz := bm.GetInterface("biz_content")
	if bzBody, ok := bz.(ltool.BodyMap); ok {
		if bodyBs, err = json.Marshal(bzBody); err != nil {
			return fmt.Errorf("json.Marshal(%v)：%w", bzBody, err)
		}
		bm.Set("biz_content", string(bodyBs))
	}

	if bs, err = a.doAliPaySelf(ctx, bm, method); err != nil {
		return err
	}
	if err = json.Unmarshal(bs, aliRsp); err != nil {
		return err
	}
	return nil
}

// 向支付宝发送自定义请求
func (a *Client) doAliPaySelf(ctx context.Context, bm ltool.BodyMap, method string) (bs []byte, err error) {
	var (
		url, sign string
	)
	bm.Set("method", method)
	// check public parameter
	a.checkPublicParam(bm)
	// check sign
	if bm.GetString("sign") == "" {
		sign, err = GetRsaSign(bm, bm.GetString("sign_type"), a.privateKey)
		if err != nil {
			return nil, fmt.Errorf("GetRsaSign Error: %w", err)
		}
		bm.Set("sign", sign)
	}
	if a.DebugSwitch == ltool.DebugOn {
		xlog.Debugf("Alipay_Request: %s", bm.JsonBody())
	}

	httpClient := xhttp.NewClient()
	if a.IsProd {
		url = baseUrlUtf8
	} else {
		url = sandboxBaseUrlUtf8
	}
	res, bs, err := httpClient.Type(xhttp.TypeForm).Post(url).SendString(bm.EncodeURLParams()).EndBytes(ctx)
	if err != nil {
		return nil, err
	}
	if a.DebugSwitch == ltool.DebugOn {
		xlog.Debugf("Alipay_Response: %s%d %s%s", xlog.Red, res.StatusCode, xlog.Reset, string(bs))
	}
	if res.StatusCode != 200 {
		return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
	}
	return bs, nil
}

func (a *Client) DoAliPay(bm ltool.BodyMap, sign_type string) (bs []byte, err error) {
	sign, err := GetRsaSign(bm, sign_type, a.privateKey)
	return []byte(sign), err
}

// 向支付宝发送请求
func (a *Client) doAliPay(ctx context.Context, bm ltool.BodyMap, method string, authToken ...string) (bs []byte, err error) {
	var (
		bodyStr string
		bodyBs  []byte
		aat     string
	)
	if bm != nil {
		aat = bm.GetString("app_auth_token")
		bm.Remove("app_auth_token")
		if bodyBs, err = json.Marshal(bm); err != nil {
			return nil, fmt.Errorf("json.Marshal：%w", err)
		}
		bodyStr = string(bodyBs)
	}
	//fmt.Println("bodyStr的信息是", bodyStr)
	pubBody := make(ltool.BodyMap)

	pubBody.Set("app_id", a.AppId).
		Set("method", method).
		Set("format", "JSON").
		Set("charset", a.Charset).
		Set("sign_type", a.SignType).
		Set("version", "1.0").
		Set("timestamp", time.Now().Format(util.TimeLayout))

	if a.AppCertSN != util.NULL {
		pubBody.Set("app_cert_sn", a.AppCertSN)
	}
	if a.AliPayRootCertSN != util.NULL {
		pubBody.Set("alipay_root_cert_sn", a.AliPayRootCertSN)
	}
	if a.ReturnUrl != util.NULL {
		pubBody.Set("return_url", a.ReturnUrl)
	}
	if a.location != nil {
		pubBody.Set("timestamp", time.Now().In(a.location).Format(util.TimeLayout))
	}
	if a.NotifyUrl != util.NULL { //如果返回url为空，传过来的返回url不为空
		//fmt.Println("url不为空？", a.NotifyUrl)
		pubBody.Set("notify_url", a.NotifyUrl)
	}
	//fmt.Println("notify,", pubBody.JsonBody())
	if aat != util.NULL {
		pubBody.Set("app_auth_token", aat)
	}
	if method == "alipay.user.info.share" {
		pubBody.Set("auth_token", authToken[0])
	}
	if bodyStr != util.NULL {
		pubBody.Set("biz_content", bodyStr)
	}
	sign, err := GetRsaSign(pubBody, pubBody.GetString("sign_type"), a.privateKey)
	if err != nil {
		return nil, fmt.Errorf("GetRsaSign Error: %w", err)
	}
	pubBody.Set("sign", sign)
	if a.DebugSwitch == ltool.DebugOn {
		xlog.Debugf("Alipay_Request: %s", pubBody.JsonBody())
	}
	param := pubBody.EncodeURLParams()
	switch method {
	case "alipay.trade.app.pay", "alipay.fund.auth.order.app.freeze":
		return []byte(param), nil
	case "alipay.trade.wap.pay", "alipay.trade.page.pay", "alipay.user.certify.open.certify":
		if !a.IsProd {
			return []byte(sandboxBaseUrl + "?" + param), nil
		}
		return []byte(baseUrl + "?" + param), nil
	case "alipay.marketing.activity.message.created":
		return a.F新的发送链接(ctx, pubBody)
	default:
		return a.DefaultRequest(ctx, param)
		/* httpClient := xhttp.NewClient()
		url = baseUrlUtf8
		if !a.IsProd {
			url = sandboxBaseUrlUtf8
		}
		res, bs, err := httpClient.Type(xhttp.TypeForm).Post(url).SendString(param).EndBytes(ctx)
		if err != nil {
			return nil, err
		}
		if a.DebugSwitch == ltool.DebugOn {
			xlog.Debugf("Alipay_Response: %s%d %s%s", xlog.Red, res.StatusCode, xlog.Reset, string(bs))
		}
		if res.StatusCode != 200 {
			return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
		}
		return bs, nil */
	}
}

func (a *Client) DefaultRequest(ctx context.Context, param string) (bs []byte, err error) {
	httpClient := xhttp.NewClient()
	url := baseUrlUtf8
	if !a.IsProd {
		url = sandboxBaseUrlUtf8
	}
	res, bs, err := httpClient.Type(xhttp.TypeForm).Post(url).SendString(param).EndBytes(ctx)
	if err != nil {
		return nil, err
	}
	if a.DebugSwitch == ltool.DebugOn {
		xlog.Debugf("Alipay_Response: %s%d %s%s", xlog.Red, res.StatusCode, xlog.Reset, string(bs))
	}
	if res.StatusCode != 200 {
		return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
	}
	return bs, nil
}

func (a *Client) F新的发送链接(ctx context.Context, bm ltool.BodyMap) (bs []byte, err error) {
	httpClient := xhttp.NewClient()
	url := baseUrlUtf8
	if !a.IsProd {
		url = sandboxBaseUrl
	}
	res, bs, err := httpClient.Type(xhttp.TypeJSON).Post(url).SendBodyMap(bm).EndBytes(ctx)
	if err != nil {
		return nil, err
	}
	if a.DebugSwitch == ltool.DebugOn {
		xlog.Debugf("Alipay_Response: %s%d %s%s", xlog.Red, res.StatusCode, xlog.Reset, string(bs))
	}
	if res.StatusCode != 200 {
		return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
	}
	return bs, nil
}

func (a *Client) FileRequest(ctx context.Context, bm ltool.BodyMap, file *ltool.File, method string) (bs []byte, err error) {

	var (
		bodyStr string
		bodyBs  []byte
		aat     string
	)

	if bm != nil {
		aat = bm.GetString("app_auth_token")
		bm.Remove("app_auth_token")
		if bodyBs, err = json.Marshal(bm); err != nil {
			return nil, fmt.Errorf("json.Marshal：%w", err)
		}
		bodyStr = string(bodyBs)
	}
	//fmt.Println("bodyStr的信息是", bodyStr)
	pubBody := make(ltool.BodyMap)
	pubBody.Set("app_id", a.AppId).
		Set("method", method).
		Set("format", "JSON").
		Set("charset", a.Charset).
		Set("sign_type", a.SignType).
		Set("version", "1.0").
		Set("timestamp", time.Now().Format(util.TimeLayout))

	if a.AppCertSN != util.NULL {
		pubBody.Set("app_cert_sn", a.AppCertSN)
	}
	if a.AliPayRootCertSN != util.NULL {
		pubBody.Set("alipay_root_cert_sn", a.AliPayRootCertSN)
	}
	if a.ReturnUrl != util.NULL {
		pubBody.Set("return_url", a.ReturnUrl)
	}
	if a.location != nil {
		pubBody.Set("timestamp", time.Now().In(a.location).Format(util.TimeLayout))
	}
	if a.NotifyUrl != util.NULL { //如果返回url为空，传过来的返回url不为空
		//fmt.Println("url不为空？", a.NotifyUrl)
		pubBody.Set("notify_url", a.NotifyUrl)
	}
	//fmt.Println("notify,", pubBody.JsonBody())
	if a.AppAuthToken != util.NULL {
		pubBody.Set("app_auth_token", a.AppAuthToken)
	}
	if aat != util.NULL {
		pubBody.Set("app_auth_token", aat)
	}
	if bodyStr != util.NULL {
		pubBody.Set("biz_content", bodyStr)
	}
	sign, err := GetRsaSign(pubBody, pubBody.GetString("sign_type"), a.privateKey)
	if err != nil {
		return nil, fmt.Errorf("GetRsaSign Error: %w", err)
	}
	//pubBody.Set("file_content", file.Content)
	pubBody.Set("sign", sign)
	if a.DebugSwitch == ltool.DebugOn {
		xlog.Debugf("Alipay_Request: %s", pubBody.JsonBody())
	}
	param := pubBody.EncodeURLParams()
	url := baseUrlUtf8 + "&" + param
	bm.Reset()
	bm.SetFormFile("file_content", file)
	httpClient := xhttp.NewClient()
	res, bs, err := httpClient.Type(xhttp.TypeMultipartFormData).Post(url).
		SendMultipartBodyMap(bm).EndBytes(ctx)
	if err != nil {
		return nil, err
	}
	if a.DebugSwitch == ltool.DebugOn {
		xlog.Debugf("Alipay_Response: %s%d %s%s", xlog.Red, res.StatusCode, xlog.Reset, string(bs))
	}
	if res.StatusCode != 200 {
		return nil, fmt.Errorf("HTTP Request Error, StatusCode = %d", res.StatusCode)
	}
	return bs, nil
}

// 公共参数检查
func (a *Client) checkPublicParam(bm ltool.BodyMap) {
	bm.Set("format", "JSON").
		Set("charset", a.Charset).
		Set("sign_type", a.SignType).
		Set("version", "1.0").
		Set("timestamp", time.Now().Format(util.TimeLayout))

	if bm.GetString("app_id") == "" && a.AppId != util.NULL {
		bm.Set("app_id", a.AppId)
	}
	if bm.GetString("app_cert_sn") == "" && a.AppCertSN != util.NULL {
		bm.Set("app_cert_sn", a.AppCertSN)
	}
	if bm.GetString("alipay_root_cert_sn") == "" && a.AliPayRootCertSN != util.NULL {
		bm.Set("alipay_root_cert_sn", a.AliPayRootCertSN)
	}
	if bm.GetString("return_url") == "" && a.ReturnUrl != util.NULL {
		bm.Set("return_url", a.ReturnUrl)
	}
	if a.location != nil {
		bm.Set("timestamp", time.Now().In(a.location).Format(util.TimeLayout))
	}
	if bm.GetString("notify_url") == "" && a.NotifyUrl != util.NULL {
		bm.Set("notify_url", a.NotifyUrl)
	}
	if bm.GetString("app_auth_token") == "" && a.AppAuthToken != util.NULL {
		bm.Set("app_auth_token", a.AppAuthToken)
	}
}

func (a *Client) F获取文件上传签名() (sign string) {
	var (
		bodyStr string
		bodyBs  []byte
		err     error
	)
	bm := make(ltool.BodyMap)
	bm.Set("scene", "SYNC_ORDER")
	if bodyBs, err = json.Marshal(bm); err != nil {
		return
	}
	bodyStr = string(bodyBs)
	//fmt.Println("bodyStr的信息是", bodyStr)
	pubBody := make(ltool.BodyMap)
	pubBody.Set("app_id", a.AppId).
		Set("method", "alipay.merchant.item.file.upload").
		Set("format", "JSON").
		Set("charset", a.Charset).
		Set("sign_type", a.SignType).
		Set("version", "1.0").
		Set("timestamp", time.Now().Format(util.TimeLayout))

	if a.AppCertSN != util.NULL {
		pubBody.Set("app_cert_sn", a.AppCertSN)
	}
	if a.AliPayRootCertSN != util.NULL {
		pubBody.Set("alipay_root_cert_sn", a.AliPayRootCertSN)
	}
	if a.ReturnUrl != util.NULL {
		pubBody.Set("return_url", a.ReturnUrl)
	}
	if a.location != nil {
		pubBody.Set("timestamp", time.Now().In(a.location).Format(util.TimeLayout))
	}
	if a.NotifyUrl != util.NULL { //如果返回url为空，传过来的返回url不为空
		//fmt.Println("url不为空？", a.NotifyUrl)
		pubBody.Set("notify_url", a.NotifyUrl)
	}
	//fmt.Println("notify,", pubBody.JsonBody())
	if a.AppAuthToken != util.NULL {
		pubBody.Set("app_auth_token", a.AppAuthToken)
	}

	if bodyStr != util.NULL {
		pubBody.Set("biz_content", bodyStr)
	}
	sign, _ = GetRsaSign(pubBody, pubBody.GetString("sign_type"), a.privateKey)
	return
}
